%{
#include <cstdio>
#include <iostream>
using namespace std;

extern "C" int yylex();
extern "C" int yyparse();
extern "C" FILE *yyin;

extern int line_num;
int err_flag = 0;

void yyerror(const char *s);
%}

%union {
	int ival;
	float fval;
	char *sval;
}

%token BOOLEAN
%token BREAK
%token CONTINUE
%token CLASS
%token DO
%token ELSE
%token EXTENDS
%token FALSE
%token FLOAT
%token FOR
%token IF
%token INT
%token NEW
%token DECAF_NULL
%token PRIVATE
%token PUBLIC
%token RETURN
%token STATIC
%token SUPER
%token THIS
%token TRUE
%token VOID
%token WHILE
%token PLUS_PLUS
%token MINUS_MINUS
%token B_AND
%token B_OR
%token B_EQ_TO
%token B_NOT_EQ_TO
%token B_LESS_THAN_EQ_TO
%token B_GREATER_THAN_EQ_TO
%token <sval> ID
%token <ival> INT_CONST
%token <fval> FLOAT_CONST
%token <sval> STRING_CONST
%token UNKNOWN
%token MULTILINE_COMMENT_ERROR

%nonassoc IF
%nonassoc ELSE
%right '='
%left B_OR
%left B_AND
%left B_EQ_TO B_NOT_EQ_TO
%nonassoc '<' '>' B_LESS_THAN_EQ_TO B_GREATER_THAN_EQ_TO
%left '+' '-'
%left '*' '/'
%nonassoc '!' UN_PLUS UN_MINUS 
 
%%

program:        
                class_decls;
class_decls:	
                /*empty*/ 
                | class_decls class_decl;
class_decl: 	
                CLASS ID inheritance '{' body_decls '}';
inheritance:	
                /*empty*/ 
                | EXTENDS ID;
body_decls:		
                body_decl 
                | body_decls body_decl;
body_decl: 			
                field_decl 
                | method_decl 
                | construct_decl;
field_decl:			
                modifier var_decl;
modifier:				
                access_specify scope;
access_specify: 
                /*empty*/ 
                | PUBLIC 
                | PRIVATE;
scope:					
                /*empty*/ 
                | STATIC;
var_decl:				
                type variables ';';
type:						
                INT	
                | FLOAT 
                | BOOLEAN	
                | ID;
variables:			
                variable 
                | variables ',' variable;
variable:				
                ID array_braces;
array_braces:		
                /*empty*/ 
                | array_braces '[' ']';

method_decl:		
                modifier type ID '(' formal_args ')' block 
                | modifier VOID ID '(' formal_args ')' block; 

construct_decl:	modifier ID '(' formal_args ')' block;

formal_args:		
                /*empty*/ 
                | formals;
formals:			
                formal_param 
                | formals ',' formal_param;
formal_param:		
                type variable;
block:              
                '{' stmts '}';
stmts:
								/*empty*/
								| stmt stmts;
stmt:
								IF '(' expr ')' stmt %prec IF
								| IF '(' expr ')' stmt ELSE stmt 
								| WHILE '(' expr ')' stmt
								| FOR '(' opt_stmt_expr ';' opt_expr ';' opt_stmt_expr ')' stmt
								| RETURN opt_expr ';'
								| stmt_expr ';'
								| BREAK	';'
								| CONTINUE ';'
								| block
								| var_decl
								| ';';

opt_stmt_expr:
								/*empty*/
								| stmt_expr;

opt_expr:
								/*empty*/
								| expr;
stmt_expr:
								assign
								| func_call;
expr:
								primary
								| new_array
								| assign
	    					| expr '+' expr /* { cout << "Reduced using expr + expr" << endl; }*/
								| expr '-' expr /*{ cout << "Reduced using expr - expr" << endl; }*/
								| expr '*' expr /*{ cout << "Reduced using expr * expr" << endl; }*/  
								| expr '/' expr /*{ cout << "Reduced using expr / expr" << endl; }*/
		        		| expr B_AND expr
								| expr B_OR expr
								| expr B_EQ_TO expr
								| expr B_NOT_EQ_TO expr
								| expr '>' expr
								| expr '<' expr
								| expr B_LESS_THAN_EQ_TO expr
								| expr B_GREATER_THAN_EQ_TO expr
	      				| '+' expr %prec UN_PLUS 
								| '-' expr %prec UN_MINUS
								| '!' expr;
assign:
								lhs '=' expr
								| lhs PLUS_PLUS
								| PLUS_PLUS lhs
								| lhs MINUS_MINUS
								| MINUS_MINUS lhs;

new_array:  
								NEW type array_opt;
array_opt:	
								multi_dimen	
								| '[' expr ']' array_opt;
multi_dimen:
								/*empty*/
								| '['']'multi_dimen;
primary:
								literal
								| THIS
								| SUPER
								| '(' expr ')'
								| NEW ID '(' opt_args ')'
								| lhs
								| func_call;
opt_args:		
								/*empty*/
								| expr  arg;
arg:		
								/*empty*/ 
								| ',' expr arg;
lhs:			
								field_access
								| array_access;
field_access:	
								primary '.' ID
								| ID;
array_access:	
								primary non_null_dimen;
func_call:		
								field_access '(' opt_args ')';
non_null_dimen:
								'[' expr ']';
literal:
                INT_CONST   /*{ cout << "bison found an int: " << $1 << endl }*/
                | FLOAT_CONST /*{ cout << "bison found a float: " << $1 << endl }*/
                | STRING_CONST /*{ cout << "bison found a string: " << $1 << endl }*/
                | DECAF_NULL /*{ cout << "bison found a null: " << endl }*/
                | TRUE  /*{ cout << "bison found a true: " << endl }*/
                | FALSE /*{ cout << "bison found a false: " << endl }*/
                ;
%%

int main() 
{
	FILE *decafSrcFile = fopen("test.decaf", "r");
	if(!decafSrcFile)
	{
		cout << "I can't open decafSrcFile" << endl;
		return -1;
	}
	yyin = decafSrcFile;
	do
	{
		yyparse();
	} while(!feof(yyin));
	
	if (1 == err_flag)
		cout<<"Parse finished with errors"<<endl;
	else
		cout<<"parse finished without errors"<<endl;
	
//cout<<"yytext:"<< YYTEXT<<endl;
	return 0;
}

void yyerror(const char *s) 
{ 
	cout << "Error: line:" << line_num <<  " Message:" << s << endl; 
	err_flag = 1;
	// might as well halt now: exit(-1);
}
